#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <string.h>
#include <cstring>
#include <sys/wait.h>
#include "Log.hpp"
#include "InetAddr.hpp"
using namespace log_ns;
enum
{
    SOCKET_ERRO = 1,
    BIND_ERRO = 2,
    LISTEN_ERR
};

const static int gport = 8888;
const static int gsock = -1;
const static int gbloklog = 8;

using command_service_t =std::function<void (int sockfd, InetAddr addr)>;
class TcpServer
{
public:
    TcpServer(command_service_t service,uint16_t port = gport)
        : _port(port)
        , _listensockfd(gsock)
        ,_Service(service)
    {
    }
    void InitServer()
    {
        // 1.创建套接字
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0); // 第二个参数为为流式套接
        if (_listensockfd < 0)
        {
            LOG(FATAL, "socket create erro\n");
            exit(SOCKET_ERRO); // 创建套接字失败
        }
        LOG(INFO, "socket create success, socket:%d\n", _listensockfd); // sockfd为3
        // 填充字段
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local)); // 初始化结构体字段
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);      // 主机转网络，不转会出错
        local.sin_addr.s_addr = INADDR_ANY; // 网络地址任意，随机地址绑定
        // 三个字段填充完毕后开始绑定
        // 2.绑定
        if (::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0) // 创建套接字后绑定，绑定前需要填充看结构体字段
        {
            LOG(FATAL, "bind erro\n");
            exit(BIND_ERRO);
        }
        LOG(INFO, "bind success\n");
        // 3.套接字设置为监听状态。
        // Tcp是面向字节流,未来需要不断地获取连接
        if (::listen(_listensockfd, gbloklog) < 0)
        {
            LOG(FATAL, "listen erro\n");
            exit(LISTEN_ERR);
        }
        LOG(INFO, "listen success\n");
    }
    class ThreadData
    {
        public:
        int _sockfd;
        TcpServer *_self;
        InetAddr _addr;
        public:
        ThreadData(int sockfd,TcpServer *self,const InetAddr &addr)
        :_sockfd(sockfd)
        ,_self(self)
        ,_addr(addr)
        {

        }
    };
    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // sleep(1);
            // 获取连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(_listensockfd, (struct sockaddr *)&client, &len); // 这是一个阻塞函数，没有新连接进入就会阻塞，直到有新连接才会返回，阻塞取消
            if (sockfd < 0)
            {
                // 获取连接失败
                LOG(WARNING, "accept erro\n");
                continue;
            }
            InetAddr addr(client);
            LOG(INFO, "get a new link,client info: %s，sockfd: %d\n", addr.AddrStr().c_str(), sockfd);
            // 版本1
            //  Service(sockfd,addr);

            // 多进程版本  2
            // 一次处理多个服务端

            // pid_t id = fork(); // 子进程创建后也会拷贝一份父进程的文件描述符表
            // if (id == 0)
            // {
            //     // 子进程
            //     ::close(_listensockfd); // 关闭listen套接字
            //     // 如果不关掉，子进程在未来可能会误操作往三号文件描述符中操作
            //     // 子进程只对4号文件描述符操作
            //     // 问题：但是如果子进程一直处于服务状态不退出，父进程就会一直阻塞等待子进程的退出，
            //     //服务还是没有满足并发（一个进程接收连接，一个进程进行服务）还是一次只能处理一个连接
            //     //解决以上问题可以创建一个孙子进程，让孙子进程变成一个孤儿进程，让操作系统回收，即可解决
            //     if(fork()>0) exit(0);//1.子进程退出
            //     Service(sockfd, addr);//2.孙子进程变为孤儿进程
            //     exit(0);
            // }
            // // father  子进程创建后父进程需要等待子进程的退出
            
            // ::close(sockfd); // 关闭自己不需要的文件描述符，防止误操作。
            // // 文件描述符的本质是一个数组下标，有上限，需要使用后对文件描述符进行关闭，防止文件描述符泄漏
            // // 一切有限资源使用后不进行释放都是对资源的一种泄漏
            // int n = waitpid(id, nullptr, 0);//3.父进程等待成功返回
            // if (n > 0)
            // {
            //     LOG(INFO, "wait child success.\n");
            // }
            //版本3：多线程
            pthread_t tid;//多线程不需要关闭不需要的文件描述符，线程之间的资源都是共享的
            ThreadData *td=new ThreadData(sockfd,this,addr);
            pthread_create(&tid, nullptr,Execute,td);//线程分离，新线程就不需要等待，也就不会造成僵尸问题
            
        }
        _isrunning = false;
    }

   static void *Execute(void *args)//设为静态即可解决this指针
    {
        pthread_detach(pthread_self());
        ThreadData *td =static_cast<ThreadData *>(args);
       td->_self->_Service(td->_sockfd,td->_addr);
        ::close(td->_sockfd);
        delete td;
        return nullptr;
    }

    // void Service(int sockfd, InetAddr addr)
    // {
    //     // 这是一个长服务
    //     while (true)
    //     {
    //         char inbuffer[1024];
    //         ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);
    //         if (n > 0)
    //         {
    //             inbuffer[n]=0;
    //             LOG(INFO,"get message from client %s ,message : %s\n",addr.AddrStr().c_str(),inbuffer);



    //             std::string echo_string = "[server echo] #";
    //             echo_string += inbuffer;
    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0)
    //         {
    //             LOG(INFO, "client %s quiT\n", addr.AddrStr().c_str());
    //             break;
    //         }
    //         else
    //         {
    //             LOG(ERROR, "read error :%s\n", addr.AddrStr().c_str());
    //         }
    //     }
    //     ::close(sockfd);
    // }

    ~TcpServer() {}

private:
    uint16_t _port; // 端口号
    int _listensockfd;
    bool _isrunning;
    command_service_t _Service;
};